/******************************************************************************
 Copyright (c) 2009-2023 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

 REVISION:      $Revision: #9 $
******************************************************************************/

#include "csr_synergy.h"

#include "csr_bt_cm_main.h"
#include "csr_bt_cm_l2cap.h"
#include "csr_bt_cm_rfc.h"
#include "csr_bt_cm_dm.h"
#include "csr_bt_cm_events_handler.h"

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
#include "csr_bt_cm_bnep.h"
#endif

#include "csr_bt_cm_util.h"
#include "csr_bt_cm_sdc.h"

/* Sniff state index for Idle power table cmPowerTableIdle */
#define CM_PT_SNIFF_STATE_IDX_IDLE      0
/* Sniff state index for Active power table cmPowerTableActive */
#define CM_PT_SNIFF_STATE_IDX_ACTIVE    2


/* 0 means infinite duration. */
#define CM_POWER_TABLE_SNIFF_DURATION_IN_SECONDS        0
#define CM_POWER_TABLE_ACTIVE_DURATION_IN_SECONDS       1
#define CM_POWER_TABLE_PASSIVE_DURATION_IN_SECONDS      1

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
static CsrBtCmPowerSetting cmPowerTableActive[] = 
{
    {LP_POWERMODE_ACTIVE, 0, 0, 0, 0, CM_POWER_TABLE_ACTIVE_DURATION_IN_SECONDS},
    {LP_POWERMODE_PASSIVE, 0, 0, 0, 0, CM_POWER_TABLE_PASSIVE_DURATION_IN_SECONDS},
    {LP_POWERMODE_SNIFF, CSR_BT_SNIFF_MIN_TIME_INTERVAL, CSR_BT_SNIFF_MAX_TIME_INTERVAL, CSR_BT_SNIFF_ATTEMPT, CSR_BT_SNIFF_TIMEOUT, CM_POWER_TABLE_SNIFF_DURATION_IN_SECONDS},
};

/* Note: for sniff mode these are the default values and will be updated based on the selected sniff settings from the existing connections. */
static CsrBtCmPowerSetting cmPowerTableIdle[] =
{
    {LP_POWERMODE_SNIFF, CSR_BT_SNIFF_MIN_TIME_INTERVAL, CSR_BT_SNIFF_MAX_TIME_INTERVAL, CSR_BT_SNIFF_ATTEMPT, CSR_BT_SNIFF_TIMEOUT, CM_POWER_TABLE_SNIFF_DURATION_IN_SECONDS},
};

static CsrBtResultCode csrBtReturnResultCode(CsrUint8 newMode)
{
    if (newMode == CSR_BT_ACTIVE_MODE)
    { /* Because the profile expect that their always can enter
         ACTIVE_MODE the CM must tell them that their can
         consider them that this request has been overruled
         because the application have taking the control or because
         all connection is running amp */
        return (CSR_BT_RESULT_CODE_CM_MODE_CHANGE_REQUEST_OVERRULED);
    }
    else
    { /* The application is not allowed to change the current link
         policy right now, or is already in this mode. Inform the
         profile */
        return (CSR_BT_RESULT_CODE_CM_SUCCESS);
    }
}

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
void CsrBtCmRfcModeChangeIndMsgSend(cmRfcConnInstType * theLogicalLink, CsrUint16 length,
                                    CsrUint8 mode, CsrBtResultCode resultCode, CsrBtSupplier resultSupplier)
{ /* Build and send CSR_BT_CM_RFC_MODE_CHANGE_IND to the application */
    CsrBtCmRfcModeChangeInd    *cmPrim;

    cmPrim = (CsrBtCmRfcModeChangeInd *)CsrPmemAlloc(sizeof(CsrBtCmRfcModeChangeInd));

    cmPrim->type            = CSR_BT_CM_RFC_MODE_CHANGE_IND;
    cmPrim->length          = length;
    cmPrim->mode            = mode;
    cmPrim->resultCode      = resultCode;
    cmPrim->resultSupplier  = resultSupplier;
    cmPrim->btConnId        = theLogicalLink->btConnId;
    cmPrim->context         = theLogicalLink->context;
    CsrBtCmPutMessage(theLogicalLink->appHandle, cmPrim);
}
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
void CsrBtCmL2caModeChangeIndMsgSend(cmL2caConnInstType *l2CaConnection, CsrUint16 length,
                                     CsrUint8 mode, CsrBtResultCode resultCode, CsrBtSupplier resultSupplier)
{ /* Build and send CSR_BT_CM_L2CA_MODE_CHANGE_IND to the application */
    CsrBtCmL2caModeChangeInd    *cmPrim;

    cmPrim = (CsrBtCmL2caModeChangeInd *)CsrPmemAlloc(sizeof(CsrBtCmL2caModeChangeInd));

    cmPrim->type            = CSR_BT_CM_L2CA_MODE_CHANGE_IND;
    cmPrim->btConnId        = l2CaConnection->btConnId;
    cmPrim->length          = length;
    cmPrim->mode            = mode;
    cmPrim->resultCode      = resultCode;
    cmPrim->resultSupplier  = resultSupplier;
    cmPrim->context         = l2CaConnection->context;
    CsrBtCmPutMessage(l2CaConnection->appHandle, cmPrim);
}
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
static void csrBtCmBnepModeChangeIndMsgSend(CsrSchedQid theAppHandle, bnepTable *bnepConnection, CsrUint16 length,
                                            CsrUint8 mode, CsrBtResultCode resultCode, CsrBtSupplier resultSupplier)
{ /* Build and send CSR_BT_CM_L2CA_MODE_CHANGE_IND to the application */
    CsrBtCmBnepModeChangeInd    *cmPrim;

    cmPrim = (CsrBtCmBnepModeChangeInd *)CsrPmemAlloc(sizeof(CsrBtCmBnepModeChangeInd));

    cmPrim->type            = CSR_BT_CM_BNEP_MODE_CHANGE_IND;
    cmPrim->id              = bnepConnection->id;
    cmPrim->length          = length;
    cmPrim->mode            = mode;
    cmPrim->resultCode      = resultCode;
    cmPrim->resultSupplier  = resultSupplier;
    CsrBtCmPutMessage(theAppHandle, cmPrim);
}
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

#ifdef CSR_BT_INSTALL_CM_LOW_POWER_CONFIG_PUBLIC
static void csrBtCmModeChangeConfigCfmMsgSend(CsrSchedQid appHandle, CsrBtResultCode resultCode,
                                              CsrBtSupplier resultSupplier, CsrBtDeviceAddr deviceAddr)
{
    CsrBtCmModeChangeConfigCfm    *cmPrim;

    cmPrim = (CsrBtCmModeChangeConfigCfm *)CsrPmemAlloc(sizeof(CsrBtCmModeChangeConfigCfm));

    cmPrim->type            = CSR_BT_CM_MODE_CHANGE_CONFIG_CFM;
    cmPrim->deviceAddr      = deviceAddr;
    cmPrim->resultCode      = resultCode;
    cmPrim->resultSupplier  = resultSupplier;
    CsrBtCmPutMessage(appHandle, cmPrim);
}
#endif

static void csrBtCmModeChangeCfmMsgSend(cmInstanceData_t *cmData, CsrUint16 interval, CsrUint8 mode,
                                        CsrBtResultCode resultCode, CsrBtSupplier resultSupplier)
{
    CsrBtCmModeChangeCfm    *cmPrim;

    cmPrim = (CsrBtCmModeChangeCfm *)CsrPmemAlloc(sizeof(CsrBtCmModeChangeCfm));

    cmPrim->type            = CSR_BT_CM_MODE_CHANGE_CFM;
    cmPrim->deviceAddr      = cmData->smVar.operatingBdAddr;
    cmPrim->interval        = interval;
    cmPrim->mode            = mode;
    cmPrim->resultCode      = resultCode;
    cmPrim->resultSupplier  = resultSupplier;
    CsrBtCmPutMessage(cmData->smVar.appHandle, cmPrim);
}

static CsrBool returnInitStateIndexToThisAddress(cmInstanceData_t *cmData,
                                                 CsrBtDeviceAddr deviceAddr,
                                                 CsrBool linkLost,
                                                 CsrBtResultCode resultCode,
                                                 CsrBtSupplier resultSupplier)
{/* Goes through the rfcomm and l2cap connections tables to see if the CM is
    in a process where it establish a releasing a connection, or the
    application has just change the link policy to active */
#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    CsrUintFast8    i;
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    cmL2caConnElement *l2caElem;
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    cmRfcConnElement *rfcElement = CsrBtCmRfcFindRfcConnElementFromDeviceAddrState1OrState2(cmData,
                                                                                            &deviceAddr,
                                                                                            CSR_BT_CM_RFC_STATE_CONNECT_INIT,
                                                                                            CSR_BT_CM_RFC_STATE_RELEASE_INIT);

    if (rfcElement)
    {
        return (TRUE);
    }

#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    for (l2caElem = CM_L2CA_GET_FIRST(cmData->l2caVar.connList); l2caElem; l2caElem = l2caElem->next)
    { /* Search through the l2ca list */
        if (l2caElem->cmL2caConnInst)
        {
            cmL2caConnInstType *l2CaConnection = l2caElem->cmL2caConnInst;

            if(CsrBtBdAddrEq(&(deviceAddr), &(l2CaConnection->deviceAddr)))
            {
                if((l2CaConnection->state == CSR_BT_CM_L2CAP_STATE_CONNECT_INIT) ||
                   (l2CaConnection->state == CSR_BT_CM_L2CAP_STATE_RELEASE_INIT) ||
                   (l2CaConnection->state == CSR_BT_CM_L2CAP_STATE_RELEASE_INIT_XOVER))
                {
                    if (linkLost && l2CaConnection->state == CSR_BT_CM_L2CAP_STATE_RELEASE_INIT)
                    {
                        l2CaConnection->state = CSR_BT_CM_L2CAP_STATE_RELEASE_INIT_XOVER;
                        cmData->smVar.arg.result.code        = resultCode;
                        cmData->smVar.arg.result.supplier    = resultSupplier;
                    }

                    return (TRUE);
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    for ( i = 0; i < CSR_BT_MAX_NUM_OF_SIMULTANEOUS_BNEP_CONNECTIONS; i++ )
    { /* Search through the BNEP connection table */
        if(CsrBtBdAddrEq(&(deviceAddr), &(cmData->bnepVar.connectVar[i].deviceAddr)))
        {
            if((cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECT_INIT) ||
               (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_DISCONNECT_INIT))
            {
                return (TRUE);
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

    if (CsrCmnListGetCount((CsrCmnList_t *) &cmData->sdcVar.sdcSearchList))
    {
        sdcSearchElement *currentElement;

        currentElement = (sdcSearchElement *) CsrCmnListGetFromIndex((CsrCmnList_t*) &cmData->sdcVar.sdcSearchList,
                                                                     cmData->sdcVar.currentElement);
        if (cmData->sdcVar.state == CSR_BT_CM_SDC_STATE_SEARCH_INIT &&
            CsrBtBdAddrEq(&deviceAddr, &currentElement->deviceAddr))
        {
            return (TRUE);
        }
    }
    return FALSE;
}

static void nextSwitchRoleHandler(cmInstanceData_t *cmData)
{
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
    CsrUint8 numOfAcl = returnNumOfAclConnection(cmData);

    if (numOfAcl > 1)
    {
        aclTable   * nextAclConnectionElement;

        returnNextAclConnectionElement(cmData, &nextAclConnectionElement);

        if (nextAclConnectionElement)
        {
#ifdef CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL
            if (nextAclConnectionElement->role == CSR_BT_MASTER_ROLE)
            {/* The role is right just make sure that MSS is not supported */
                CsrBtCmDmWriteLpSettingsOnlyLowPowerMsgSend(cmData, nextAclConnectionElement->deviceAddr, CM_PLAYER);
                cmData->roleVar.roleSwitchFlag        = FALSE;
                nextAclConnectionElement->roleChecked = TRUE;
            }
            else
#endif
            { /* Need to change make a role switch */
                CsrBtCmDmModeChangeRoleSwitchHandler(cmData, nextAclConnectionElement->deviceAddr);
            }
        }
        else
        { /* The process is finish restore the queues */
            CsrBtCmDmLocalQueueHandler();
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
        }
    }
    else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
    {/* The process is finish restore the queues */
        CsrBtCmDmLocalQueueHandler();
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }
}

static void switchRoleStateHandler(cmInstanceData_t *cmData, CsrUint8 theMode, CsrBtDeviceAddr theAddr)
{
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
    if(theMode == CSR_BT_ACTIVE_MODE)
    { /* The CM has force the link in ACTIVE mode in order to
         be able to perform the role switch */
        cmData->roleVar.roleSwitchFlag                  = TRUE;
        CsrBtCmDmWriteLpSettingsForceLinkPolicyMsgSend(cmData, theAddr, CM_PLAYER, ENABLE_MS_SWITCH);
    }
    else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
    { /* Has just change the link policy back to CSR_BT_SNIFF_MODE.
         Found out if more ACL connection need to make a role switch */
        cmData->dmVar.roleSwitchLinkMode                = CSR_BT_ACTIVE_MODE;
        nextSwitchRoleHandler(cmData);
    }
}

static void csrBtCmDmInitRfcScoStateHandler(cmInstanceData_t *cmData, CsrBtDeviceAddr theAddr)
{/* The CM has force the link in ACTIVE mode in order to be able to perform
    the role switch */
    cmData->smVar.activePlayer            = RFC_PLAYER;
    cmData->dmVar.switchRoleDeviceAddr    = theAddr;
    dm_hci_switch_role(&theAddr, CSR_BT_MASTER_ROLE, NULL);
}

static void cmDmNotifyModeChangeErrorToApp(cmInstanceData_t                *cmData,
                                                DM_HCI_MODE_CHANGE_EVENT_IND_T  *dmPrim,
                                                CsrUint8                         requestedMode,
                                                CsrBtResultCode                 resultCode,
                                                CsrBtSupplier             resultSupplier)
{ /* It has not possible to change the current link policy. Inform all
     involved applications */
#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    CsrUintFast8 i;
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    cmL2caConnElement *currentL2caElem;
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    cmRfcConnElement *currentRfcElem;

    for (currentRfcElem = CM_RFC_GET_FIRST(cmData->rfcVar.connList); currentRfcElem; currentRfcElem = currentRfcElem->next)
    { /* Search through the RFC connection table */
        if (currentRfcElem->cmRfcConnInst)
        {
            if (currentRfcElem->cmRfcConnInst->state == CSR_BT_CM_RFC_STATE_CONNECTED)
            { /* The application has a RFCOMM connection */
                if(CsrBtBdAddrEq(&(dmPrim->bd_addr), &(currentRfcElem->cmRfcConnInst->deviceAddr)))
                {/* The given device address is right. */
                    if(currentRfcElem->cmRfcConnInst->requestedMode == requestedMode)
                    { /* This application is involved. Build and send
                         CSR_BT_CM_RFC_MODE_CHANGE_IND to it */
                        cmRfcConnInstType *theLogicalLink   = currentRfcElem->cmRfcConnInst;

                        CsrBtCmRfcModeChangeIndMsgSend(theLogicalLink,
                                                       dmPrim->length, dmPrim->mode, resultCode, resultSupplier);
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    for (currentL2caElem = CM_L2CA_GET_FIRST(cmData->l2caVar.connList); currentL2caElem; currentL2caElem = currentL2caElem->next)
    { /* Search through the L2CAP connection list */
        if (currentL2caElem->cmL2caConnInst)
        {
            if (currentL2caElem->cmL2caConnInst->state == CSR_BT_CM_L2CAP_STATE_CONNECTED)
            { /* The application has a L2CAP connection */
                if(CsrBtBdAddrEq(&(dmPrim->bd_addr), &(currentL2caElem->cmL2caConnInst->deviceAddr)))
                { /* The given device address is right. */
                    if(currentL2caElem->cmL2caConnInst->requestedMode == requestedMode)
                    { /* This application is involved. Build and send
                         CSR_BT_CM_L2CA_MODE_CHANGE_IND to it */
                        cmL2caConnInstType *l2CaConnection = currentL2caElem->cmL2caConnInst;

                        CsrBtCmL2caModeChangeIndMsgSend(l2CaConnection, dmPrim->length,
                                                        dmPrim->mode, resultCode, resultSupplier);
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    for( i = 0; i < CSR_BT_MAX_NUM_OF_SIMULTANEOUS_BNEP_CONNECTIONS; i++ )
    { /* Search through the BNEP connection table */
        if (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED)
        { /* The application has a BNEP connection */
            if(CsrBtBdAddrEq(&(dmPrim->bd_addr), &(cmData->bnepVar.connectVar[i].deviceAddr)))
            { /* The given device address is right. */
                if(cmData->bnepVar.connectVar[i].requestedMode == requestedMode)
                { /* This application is involved. Build and send
                     CSR_BT_CM_BNEP_MODE_CHANGE_IND to it */
                    bnepTable *bnepConnection;

                    bnepConnection    = &(cmData->bnepVar.connectVar[i]);
                    csrBtCmBnepModeChangeIndMsgSend(cmData->bnepVar.appHandle,
                                                    bnepConnection, dmPrim->length, dmPrim->mode, resultCode, resultSupplier);
                    cmData->bnepVar.connectVar[i].actualMode = dmPrim->mode;
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */
}

static void cmDmNotifyModeChangeToApp(cmInstanceData_t *cmData,
                                       CsrBtDeviceAddr deviceAddr, CsrUint8 mode, CsrUint16 length)
{ /* Inform all involved applications about the current link policy */
#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    CsrUintFast8 i;
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */
    CsrBool       enterSsr = CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData, deviceAddr) > 0 &&
        mode == CSR_BT_SNIFF_MODE && cmData->dmVar.lmpVersion >= CSR_BT_BLUETOOTH_VERSION_2P1
        ? TRUE : FALSE;

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    cmL2caConnElement *currentL2caElem;
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    cmRfcConnElement *currentRfcElem;

    for (currentRfcElem = CM_RFC_GET_FIRST(cmData->rfcVar.connList); currentRfcElem; currentRfcElem = currentRfcElem->next)
    { /* Search through the RFC connection table */
        if (currentRfcElem->cmRfcConnInst)
        {
            if (currentRfcElem->cmRfcConnInst->state == CSR_BT_CM_RFC_STATE_CONNECTED ||
                currentRfcElem->cmRfcConnInst->state == CSR_BT_CM_RFC_STATE_RELEASE_INIT)
            { /* The application has a RFCOMM connection */
                if(CsrBtBdAddrEq(&(deviceAddr), &(currentRfcElem->cmRfcConnInst->deviceAddr)))
                { /* The given device address is right. Build and send
                     CSR_BT_CM_RFC_MODE_CHANGE_IND to the application with the current
                     link policy. */
                    cmRfcConnInstType *theLogicalLink   = currentRfcElem->cmRfcConnInst;

                    if (theLogicalLink->state == CSR_BT_CM_RFC_STATE_CONNECTED)
                    {
                        CsrBtCmRfcModeChangeIndMsgSend(theLogicalLink, length, mode,
                                                       CSR_BT_RESULT_CODE_CM_SUCCESS, CSR_BT_SUPPLIER_CM);
                    }

                    if (mode == CSR_BT_SNIFF_MODE && theLogicalLink->state == CSR_BT_CM_RFC_STATE_CONNECTED)
                    {
#if CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1 && defined(CSR_BT_INSTALL_CM_PRI_MODE_SETTINGS)
                        if(theLogicalLink->ssrSettings.maxRemoteLatency  != CM_SSR_DISABLED &&
                           theLogicalLink->ssrAccepted == TRUE)
                        {
                            /* do nothing - at the end of this function SSR will be activated if no */
                            /* other connections exist which can not accept SSR */
                        }
                        else
#endif /* CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1 && CSR_BT_INSTALL_CM_PRI_MODE_SETTINGS */
                        {
                            /* connection which does not want SSR exist */
                            enterSsr = FALSE;
                        }
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    for (currentL2caElem = CM_L2CA_GET_FIRST(cmData->l2caVar.connList); currentL2caElem; currentL2caElem = currentL2caElem->next)
    { /* Search through the L2CAP connection list */
        if (currentL2caElem->cmL2caConnInst)
        {
            cmL2caConnInstType *l2CaConnection = currentL2caElem->cmL2caConnInst;

            if(l2CaConnection->state == CSR_BT_CM_L2CAP_STATE_CONNECTED ||
               l2CaConnection->state == CSR_BT_CM_L2CAP_STATE_RELEASE_INIT)
            {/* The application has a valid L2CAP connection */
                if(CsrBtBdAddrEq(&(deviceAddr), &(l2CaConnection->deviceAddr)))
                { /* The given device address is right. Build and send
                     CSR_BT_CM_L2CA_MODE_CHANGE_IND to the application with the current
                     link policy. */
                    if (l2CaConnection->state == CSR_BT_CM_L2CAP_STATE_CONNECTED)
                    {
                        CsrBtCmL2caModeChangeIndMsgSend(l2CaConnection, length, mode,
                                                        CSR_BT_RESULT_CODE_CM_SUCCESS, CSR_BT_SUPPLIER_CM);

                        if(mode == CSR_BT_SNIFF_MODE)
                        {
#if CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1 && defined(CSR_BT_INSTALL_CM_PRI_MODE_SETTINGS)
                            if(l2CaConnection->ssrSettings.maxRemoteLatency != CM_SSR_DISABLED &&
                               l2CaConnection->ssrAccepted == TRUE)
                            {
                                /* do nothing - at the end of this function SSR will be activated if no */
                                /* other connections exist which can not accept SSR */
                            }
                            else
#endif /* CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1 && CSR_BT_INSTALL_CM_PRI_MODE_SETTINGS */
                            {
                                /* connection which does not want SSR exist */
                                enterSsr = FALSE;
                            }
                        }
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    for( i = 0; i < CSR_BT_MAX_NUM_OF_SIMULTANEOUS_BNEP_CONNECTIONS; i++ )
    { /* Search through the BNEP connection table */

        if (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED ||
            cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_DISCONNECT_INIT)
        {
            if(CsrBtBdAddrEq(&(deviceAddr), &(cmData->bnepVar.connectVar[i].deviceAddr)))
            { /* Thie given address is right. Store the actual link policy in the connection table */
                bnepTable *bnepConnection;

                bnepConnection    = &(cmData->bnepVar.connectVar[i]);
                cmData->bnepVar.connectVar[i].actualMode = mode;

                if (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED)
                { /* Inform the application with the current link policy */
                    csrBtCmBnepModeChangeIndMsgSend(cmData->bnepVar.appHandle, bnepConnection,
                                                    length, mode, CSR_BT_RESULT_CODE_CM_SUCCESS, CSR_BT_SUPPLIER_CM);
                }

                if (mode == CSR_BT_SNIFF_MODE &&
                    cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED)
                {
#if CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1 && defined(CSR_BT_INSTALL_CM_PRI_MODE_SETTINGS)

                    if(bnepConnection->ssrSettings.maxRemoteLatency != CM_SSR_DISABLED &&
                       bnepConnection->ssrAccepted == TRUE)
                    {
                        /* do nothing - at the end of this function SSR will be activated if no */
                        /* other connections exist which can not accept SSR */
                    }
                    else
#endif /* CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1 && CSR_BT_INSTALL_CM_PRI_MODE_SETTINGS */
                    {
                        /* connection which does not want SSR exist */
                        enterSsr = FALSE;
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

#if CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1
    if (enterSsr == TRUE)
    {
        aclTable *aclConnectionElement;

        returnAclConnectionElement(cmData, deviceAddr, &aclConnectionElement);

        if (aclConnectionElement && aclConnectionElement->appControlsLowPower == TRUE)
        { /* The Application is controlling low power, no need to check sniff
             sub rating parameters */
            ;
        }
        else
        {
            CsrBtCmDmCheckSsrReqSend(deviceAddr);
        }
    }
#endif /* CSR_BT_BT_VERSION >= CSR_BT_BLUETOOTH_VERSION_2P1 */
}

static CsrBool csrBtCmModeChangeAllowed(aclTable   *aclConnectionElement)
{
    if (!aclConnectionElement->appControlsLowPower)
    {
        return (TRUE);
    }
    else
    {
        return (FALSE);
    }
}

static void csrBtCmDmStoreActualMode(cmInstanceData_t *cmData)
{ /* Store the actual mode identify by the given device address */
    DM_HCI_MODE_CHANGE_EVENT_IND_T *dmPrim;
    aclTable *aclConnectionElement;

    dmPrim    = (DM_HCI_MODE_CHANGE_EVENT_IND_T *) cmData->recvMsgP;

    returnAclConnectionElement(cmData, dmPrim->bd_addr, &aclConnectionElement);

    if (aclConnectionElement)
    {
        aclConnectionElement->mode     = dmPrim->mode;
        aclConnectionElement->interval = dmPrim->length;
    }
}

static CsrBool csrBtCmRequestedModeSupported(aclTable *aclConnectionElement, CsrUint8 requestedMode)
{
    switch (requestedMode)
    {
        case CSR_BT_ACTIVE_MODE:
            {
                return TRUE;
            }
        case CSR_BT_SNIFF_MODE:
            {
                if (aclConnectionElement)
                {
                    if ((aclConnectionElement->linkPolicySettings & ENABLE_SNIFF) == ENABLE_SNIFF)
                    {
                        return TRUE;
                    }
                }
                else
                {
                    return TRUE;
                }
            }
        default:
            {
                /* Fall through */
            }
    }

    return FALSE;
}

CsrUint8 CsrBtCmDmReturnRequestedMode(cmInstanceData_t *cmData, CsrBtDeviceAddr theAddr)
{ /* Return the link policy for the given device address */
#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    cmRfcConnElement *currentRfcElem;
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    cmL2caConnElement *currentL2caElem;
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    CsrUintFast8 i;
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

    aclTable   *aclConnectionElement;

    returnAclConnectionElement(cmData, theAddr, &aclConnectionElement);

    if (aclConnectionElement)
    {
        if (aclConnectionElement->linkPolicySettings == DISABLE_ALL_LM_MODES)
        {
            return CSR_BT_ACTIVE_MODE;
        }
    }

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    for (currentRfcElem = CM_RFC_GET_FIRST(cmData->rfcVar.connList); currentRfcElem; currentRfcElem = currentRfcElem->next)
    {
        if (currentRfcElem->cmRfcConnInst)
        {
            if(CsrBtBdAddrEq(&(theAddr), &(currentRfcElem->cmRfcConnInst->deviceAddr)))
            {
                if (CSR_BT_CM_RFC_STATE_CONNECTED == currentRfcElem->cmRfcConnInst->state)
                {
                    switch(currentRfcElem->cmRfcConnInst->requestedMode)
                    {
                        case CSR_BT_ACTIVE_MODE :
                            {/* The link policy must be ACTIVE */
                                return CSR_BT_ACTIVE_MODE;
                            }
                        default :
                            {
                                break;
                            }
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    for (currentL2caElem = CM_L2CA_GET_FIRST(cmData->l2caVar.connList); currentL2caElem; currentL2caElem = currentL2caElem->next)
    { /* Search through the L2CAP connection list */
        if (currentL2caElem->cmL2caConnInst)
        {
            if (currentL2caElem->cmL2caConnInst->state == CSR_BT_CM_L2CAP_STATE_CONNECTED)
            { /* The application has a l2cap connection */
                if(CsrBtBdAddrEq(&(theAddr), &(currentL2caElem->cmL2caConnInst->deviceAddr)))
                { /* The given device address is right */
                    switch(currentL2caElem->cmL2caConnInst->requestedMode)
                    {
                        case CSR_BT_ACTIVE_MODE :
                            {/* The link policy must be ACTIVE */
                                return CSR_BT_ACTIVE_MODE;
                            }
                        default :
                            {
                                break;
                            }
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    for( i = 0; i < CSR_BT_MAX_NUM_OF_SIMULTANEOUS_BNEP_CONNECTIONS; i++ )
    { /* Search through the BNEP connection table */
        if (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED)
        { /* The application has a bnep connection */
            if(CsrBtBdAddrEq(&(theAddr), &(cmData->bnepVar.connectVar[i].deviceAddr)))
            { /* The given device address is right */
                switch(cmData->bnepVar.connectVar[i].requestedMode)
                {
                    case CSR_BT_ACTIVE_MODE :
                        {/* The link policy must be ACTIVE */
                            return CSR_BT_ACTIVE_MODE;
                        }
                    default:
                        {
                            break;
                        }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

    if (CsrCmnListSearch((CsrCmnList_t *) &cmData->sdcVar.sdcSearchList,
                         CsrBtCmSdcSearchListAddrCompare,
                         &theAddr))
    {
        return CSR_BT_ACTIVE_MODE;
    }

    if (aclConnectionElement)
    {
        if ((aclConnectionElement->linkPolicySettings & ENABLE_SNIFF) != ENABLE_SNIFF)
        { /* SNIFF MODE is currently not supported return the Active mode */
            return CSR_BT_ACTIVE_MODE;
        }
    }
    return CSR_BT_SNIFF_MODE;
}

static void csrBtCmReturnLowPowerInterval(cmInstanceData_t *cmData, CsrBtDeviceAddr devAddr,
                                          CsrBtSniffSettings *sniffSettings)
{
#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    cmRfcConnElement *currentRfcElem;
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    cmL2caConnElement *currentL2caElem;
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    CsrUintFast8 i;
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

    CsrUint8 maxPriorityDetected = LOW_POWER_DEFAULT_PRIORITY;

    *sniffSettings              = cmData->dmVar.defaultSniffSettings;

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    for (currentRfcElem = CM_RFC_GET_FIRST(cmData->rfcVar.connList); currentRfcElem; currentRfcElem = currentRfcElem->next)
    {
        if (currentRfcElem->cmRfcConnInst)
        {
            if(CsrBtBdAddrEq(&(devAddr), &(currentRfcElem->cmRfcConnInst->deviceAddr)))
            { /* The given device address is right. Check mode settings */
                if(currentRfcElem->cmRfcConnInst->lowPowerPriority <= maxPriorityDetected)
                {
                    cmRfcConnInstType *theLogicalLink   = currentRfcElem->cmRfcConnInst;

                    maxPriorityDetected = theLogicalLink->lowPowerPriority;
                    *sniffSettings      = theLogicalLink->sniffSettings;
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    for (currentL2caElem = CM_L2CA_GET_FIRST(cmData->l2caVar.connList); currentL2caElem; currentL2caElem= currentL2caElem->next)
    { /* Search through the L2CAP connection list */
        if (currentL2caElem->cmL2caConnInst)
        {
            if(CsrBtBdAddrEq(&(devAddr), &(currentL2caElem->cmL2caConnInst->deviceAddr)))
            { /* The given device address is right. Check mode settings */
                if(currentL2caElem->cmL2caConnInst->lowPowerPriority <= maxPriorityDetected)
                {
                    cmL2caConnInstType *l2CaConnection = currentL2caElem->cmL2caConnInst;

                    maxPriorityDetected = l2CaConnection->lowPowerPriority;
                    *sniffSettings      = l2CaConnection->sniffSettings;
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    for( i = 0; i < CSR_BT_MAX_NUM_OF_SIMULTANEOUS_BNEP_CONNECTIONS; i++ )
    { /* Search through the BNEP connection table */

        if (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED ||
            cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_DISCONNECT_INIT)
        {
            if(CsrBtBdAddrEq(&(devAddr), &(cmData->bnepVar.connectVar[i].deviceAddr)))
            { /* Thie given address is right. Store the actual link policy in the connection table */
                if(cmData->bnepVar.connectVar[i].lowPowerPriority <= maxPriorityDetected)
                {
                    bnepTable *bnepConnection;

                    bnepConnection      = &(cmData->bnepVar.connectVar[i]);
                    maxPriorityDetected = bnepConnection->lowPowerPriority;
                    *sniffSettings      = bnepConnection->sniffSettings;
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */
}

#ifdef CSR_BT_INSTALL_CM_LOW_POWER_CONFIG_PUBLIC
static void csrBtCmChangeAllAclConnectionsAppControlsLowPower(cmInstanceData_t *cmData, CsrBool appControlsLowPower)
{
    CsrUintFast8 i;

    for (i = 0; i < NUM_OF_ACL_CONNECTION; i++)
    {
        if (!CsrBtBdAddrEqZero(&(cmData->roleVar.aclVar[i].deviceAddr)))
        {
            cmData->roleVar.aclVar[i].appControlsLowPower = appControlsLowPower;

            if (appControlsLowPower == FALSE)
            { /* The application has diable low power control.
                 Make sure that the profile receives the right
                 actual link mode */
                cmDmNotifyModeChangeToApp(cmData, cmData->roleVar.aclVar[i].deviceAddr,
                                           cmData->roleVar.aclVar[i].mode, cmData->roleVar.aclVar[i].interval);
            }
        }
    }
}


static CsrBtResultCode csrBtCmUpdateControlLowPowerParameter(cmInstanceData_t *cmData,
                                                             CsrBtDeviceAddr deviceAddr, CsrBool controlField)
{
    if (CsrBtBdAddrEqZero(&deviceAddr))
    { /* Either will the application have full low power control
         or it will not control low power control at all */
        cmData->dmVar.appControlsAllLowPower = controlField;
        csrBtCmChangeAllAclConnectionsAppControlsLowPower(cmData, controlField);
        return (CSR_BT_RESULT_CODE_CM_SUCCESS);
    }
    else
    { /* The application will only have low power control on a
         specific ACL connection or disable low power control
         on this ACL connection */
        aclTable            *aclConnectionElement;

        returnAclConnectionElement(cmData, deviceAddr, &aclConnectionElement);

        if (aclConnectionElement)
        { /* The ACL connection represented by the given device
             address is connected */
            aclConnectionElement->appControlsLowPower = controlField;

            if (controlField == FALSE)
            { /* The application has diable low power control.
                 Make sure that the profile receives the right
                 actual link mode */
                cmDmNotifyModeChangeToApp(cmData, deviceAddr,
                                           aclConnectionElement->mode, aclConnectionElement->interval);
            }
            return (CSR_BT_RESULT_CODE_CM_SUCCESS);
        }
        else
        { /* The ACL connection represented by the given device
             address is not connected */
            return (CSR_BT_RESULT_CODE_CM_UNKNOWN_CONNECTION_IDENTIFIER);
        }
    }
}

void CsrBtCmModeChangeConfigReqHandler(cmInstanceData_t *cmData)
{
    CsrBtResultCode            result;
    CsrBtCmModeChangeConfigReq * cmPrim;

    cmPrim = (CsrBtCmModeChangeConfigReq *) cmData->recvMsgP;

    if ((cmPrim->config & CSR_BT_CM_MODE_CHANGE_ENABLE) == CSR_BT_CM_MODE_CHANGE_ENABLE)
    { /* The application wants full low power control */
        result = csrBtCmUpdateControlLowPowerParameter(cmData, cmPrim->deviceAddr, TRUE);
    }
    else if ((cmPrim->config & CSR_BT_CM_MODE_CHANGE_DISABLE) == CSR_BT_CM_MODE_CHANGE_DISABLE)
    { /* The application wants to disable low power control */
        result = csrBtCmUpdateControlLowPowerParameter(cmData, cmPrim->deviceAddr, FALSE);
    }
    else
    { /* Invalid config parameter inform the application */
        result = CSR_BT_RESULT_CODE_CM_UNACCEPTABLE_PARAMETER;
    }
    csrBtCmModeChangeConfigCfmMsgSend(cmPrim->phandle, result, CSR_BT_SUPPLIER_CM, cmPrim->deviceAddr);
}

void CsrBtCmModeChangeReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmModeChangeReq    *cmPrim;
    aclTable                *aclConnectionElement;
    CsrBool                  unlock = TRUE;

    cmPrim = (CsrBtCmModeChangeReq *) cmData->recvMsgP;

    cmData->smVar.appHandle             = cmPrim->phandle;

    if (returnAclConnectionElement(cmData, cmPrim->deviceAddr, &aclConnectionElement) != CM_ERROR)
    {
        if (cmPrim->mode == CSR_BT_SNIFF_MODE || cmPrim->mode == CSR_BT_ACTIVE_MODE)
        {
            if (aclConnectionElement->mode != cmPrim->mode)
            {
                /* Requested mode is not present, try to update the power table for the same. */
                CmDmForceUpdatePowerTable(cmData,
                                          &cmPrim->deviceAddr,
                                          cmPrim->mode,
                                          &cmPrim->sniffSettings,
                                          CM_PT_STATE_APP);
                /* SM queue will be unlocked once the requested mode change occurs. */
                unlock = FALSE;
            }
            else
            {
                /* Current and the requested modes are same. If sniff mode is requested check if the sniff
                 * settings needs to be updated or not. */
                if (cmPrim->mode == CSR_BT_SNIFF_MODE)
                {
                    if (cmPrim->forceSniffSettings)
                    {
                        CmDmForceUpdatePowerTable(cmData,
                                                  &cmPrim->deviceAddr,
                                                  cmPrim->mode,
                                                  &cmPrim->sniffSettings,
                                                  CM_PT_STATE_APP);
                        /* SM queue will be unlocked once the requested mode change occurs. */
                        unlock = FALSE;
                    }
                }
                else
                {
                    /* As requested mode and current mode are same, report to the application saying nothing needs to be done. */
                    csrBtCmModeChangeCfmMsgSend(cmData,
                                                aclConnectionElement->interval,
                                                aclConnectionElement->mode,
                                                CSR_BT_RESULT_CODE_CM_SUCCESS,
                                                CSR_BT_SUPPLIER_CM);
                }
            }
        }
        else
        {
            /* The mode requested by the application is invalid */
            csrBtCmModeChangeCfmMsgSend(cmData,
                                        aclConnectionElement->interval,
                                        aclConnectionElement->mode,
                                        CSR_BT_RESULT_CODE_CM_UNACCEPTABLE_PARAMETER,
                                        CSR_BT_SUPPLIER_CM);
        }
    }
    else
    {
        /* ACL connection doesn't exists, the mode cannot be changed. */
        csrBtCmModeChangeCfmMsgSend(cmData,
                                    0,
                                    CSR_BT_ACTIVE_MODE,
                                    CSR_BT_RESULT_CODE_CM_UNKNOWN_CONNECTION_IDENTIFIER,
                                    CSR_BT_SUPPLIER_CM);
    }

    if (unlock)
    {
        /* Restore the local service manager queue */
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }
}
#endif /* CSR_BT_INSTALL_CM_LOW_POWER_CONFIG_PUBLIC */

static void cmDmUpdatePowerTableSettings(CsrBtSniffSettings *sniffSettings)
{
    if (sniffSettings)
    {
        /* Sniff settings of both the tables needs to be updated, as the settings are
         * associated with the mode and not table. */
        cmPowerTableIdle[CM_PT_SNIFF_STATE_IDX_IDLE].min_interval = sniffSettings->min_interval;
        cmPowerTableIdle[CM_PT_SNIFF_STATE_IDX_IDLE].max_interval = sniffSettings->max_interval;
        cmPowerTableIdle[CM_PT_SNIFF_STATE_IDX_IDLE].timeout      = sniffSettings->timeout;
        cmPowerTableIdle[CM_PT_SNIFF_STATE_IDX_IDLE].attempt      = sniffSettings->attempt;

        cmPowerTableActive[CM_PT_SNIFF_STATE_IDX_ACTIVE].min_interval = sniffSettings->min_interval;
        cmPowerTableActive[CM_PT_SNIFF_STATE_IDX_ACTIVE].max_interval = sniffSettings->max_interval;
        cmPowerTableActive[CM_PT_SNIFF_STATE_IDX_ACTIVE].timeout      = sniffSettings->timeout;
        cmPowerTableActive[CM_PT_SNIFF_STATE_IDX_ACTIVE].attempt      = sniffSettings->attempt;
    }
}

static void cmUpdatePowerTable(CsrBtCmPowerSetting *powerTable, CsrBtDeviceAddr *deviceAddr, CsrUint8 powerTableRows)
{
    dm_lp_write_powerstates_req(deviceAddr,
                                powerTableRows,
                                CsrMemDup(powerTable, powerTableRows * sizeof(powerTable[0])), NULL);
}

void CmDmForceUpdatePowerTable(cmInstanceData_t *cmData,
                               CsrBtDeviceAddr *deviceAddr,
                               CsrUint8 requestedMode,
                               CsrBtSniffSettings *requestedSniffSettings,
                               CmPtState ptState)
{
    /* This is the place where we are updating the power table,
     * hence the requested mode will be set to the final value here. */
    cmData->smVar.requestedMode = requestedMode;
    cmData->smVar.operatingBdAddr = *deviceAddr;
    CM_PT_STATE_SET(cmData, ptState);

    if (requestedMode == CSR_BT_ACTIVE_MODE)
    {
        cmUpdatePowerTable(cmPowerTableActive, deviceAddr, sizeof(cmPowerTableActive) / sizeof(cmPowerTableActive[0]));
    }
    else
    {
        /* Update the selected sniff settings to the power table and send it to bluestack. */
        CsrBtSniffSettings sniffSettings;

        /* Check if caller has requested specific sniff settings to be applied or not */
        if (requestedSniffSettings)
        {
            sniffSettings = *requestedSniffSettings;
        }
        else
        {
            /* Derivation of sniff settings from the existing connections. */
            csrBtCmReturnLowPowerInterval(cmData, *deviceAddr, &sniffSettings);
        }

        cmDmUpdatePowerTableSettings(&sniffSettings);
        cmUpdatePowerTable(cmPowerTableIdle, deviceAddr, sizeof(cmPowerTableIdle) / sizeof(cmPowerTableIdle[0]));
    }
}

CsrBtResultCode CmDmUpdatePowerTable(cmInstanceData_t *cmData,
                                     CsrBtDeviceAddr *deviceAddr,
                                     CsrUint8 requestedMode,
                                     CsrUint8 *mode,
                                     CmPtState ptState)
{
    CsrBtResultCode resultCode = CSR_BT_RESULT_CODE_CM_SUCCESS;
    aclTable *aclConnectionElement;

    if (returnAclConnectionElement(cmData, *deviceAddr, &aclConnectionElement) != CM_ERROR)
    {
        CsrUint8 newMode;

        /* Check link policy settings to see if requested mode is supported or not. */
        if (requestedMode != CSR_BT_PASSIVE_MODE &&
            !csrBtCmRequestedModeSupported(aclConnectionElement, requestedMode))
        {
            return CSR_BT_RESULT_CODE_CM_UNSUPPORTED_FEATURE;
        }

        /* Voting is triggered only for SNIFF mode and not required for ACTIVE mode. */
        newMode = (requestedMode != CSR_BT_ACTIVE_MODE) ?
                  CsrBtCmDmReturnRequestedMode(cmData, *deviceAddr) :
                  requestedMode;

        if (mode)
        {
            /* Update selected mode for the caller. */
            *mode = newMode;
        }

        /* If app is controlling low power then mode change is not allowed from profiles. */
        if (!csrBtCmModeChangeAllowed(aclConnectionElement))
        {
            return CSR_BT_RESULT_CODE_CM_COMMAND_DISALLOWED;
        }

        if (aclConnectionElement->mode != newMode)
        {
            CmDmForceUpdatePowerTable(cmData,
                                      deviceAddr,
                                      newMode,
                                      NULL,
                                      ptState);
            /* Caller may want to wait for the mode change to complete. */
            resultCode = RESULT_CODE_CM_POWER_TABLE_UPDATED;
        }
    }
    else
    {
        /* If there is no acl connection, mode change cannot happen. */
        resultCode = CSR_BT_RESULT_CODE_CM_UNKNOWN_CONNECTION_IDENTIFIER;
    }

    return resultCode;
}

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
void CsrBtCmRfcModeChangeReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmRfcModeChangeReq *cmPrim;
    cmRfcConnElement *theElement;
    CsrBool unlock = TRUE;
    aclTable *aclConnectionElement;

    cmPrim = (CsrBtCmRfcModeChangeReq *) cmData->recvMsgP;
    theElement = CM_FIND_RFC_ELEMENT(CsrBtCmRfcFindRfcConnElementFromBtConnId,
                                     &(cmPrim->btConnId));

    if (theElement &&
        theElement->cmRfcConnInst &&
        returnAclConnectionElement(cmData, theElement->cmRfcConnInst->deviceAddr, &aclConnectionElement) != CM_ERROR)
    {
        cmRfcConnInstType *rfcLink = theElement->cmRfcConnInst;

        if (rfcLink->state == CSR_BT_CM_RFC_STATE_CONNECTED)
        {
            CsrBtResultCode resultCode;
            CsrUint8 newMode = CSR_BT_ACTIVE_MODE;

            /* Store the requested mode for the voting process */
            rfcLink->requestedMode = cmPrim->requestedMode;

            /* By default the power table uses default sniff settings, only in case of a
             * sniff mode the settings needs to be checked for update. */
            resultCode = CmDmUpdatePowerTable(cmData,
                                              &rfcLink->deviceAddr,
                                              cmPrim->requestedMode,
                                              &newMode,
                                              CM_PT_STATE_RFC_CONNECT_OR_DISCONNECT);

            if (resultCode == RESULT_CODE_CM_POWER_TABLE_UPDATED)
            {
                /* Power table update is required, wait for the mode change. */
                unlock = FALSE;
            }
            else if (resultCode == CSR_BT_RESULT_CODE_CM_COMMAND_DISALLOWED)
            {
                resultCode = csrBtReturnResultCode(newMode);

                CsrBtCmRfcModeChangeIndMsgSend(rfcLink,
                                               aclConnectionElement->interval,
                                               aclConnectionElement->mode,
                                               resultCode,
                                               CSR_BT_SUPPLIER_CM);
            }
            else
            {
                CsrBtCmRfcModeChangeIndMsgSend(rfcLink,
                                               aclConnectionElement->interval,
                                               aclConnectionElement->mode,
                                               resultCode,
                                               CSR_BT_SUPPLIER_CM);
            }
        }
    }

    if (unlock)
    {
        /* Restore the local service manager queue */
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }
}
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
void CsrBtCmL2caModeChangeReqHandler(cmInstanceData_t *cmData)
{
    aclTable *aclConnectionElement;
    CsrBtCmL2caModeChangeReq    *cmPrim = (CsrBtCmL2caModeChangeReq *) cmData->recvMsgP;
    cmL2caConnElement * theElement      = CM_FIND_L2CA_ELEMENT(CsrBtCmL2caFindL2caConnElementFromBtConnId, &(cmPrim->btConnId));
    CsrBool unlock = TRUE;

    if (theElement &&
        theElement->cmL2caConnInst &&
        returnAclConnectionElement(cmData, theElement->cmL2caConnInst->deviceAddr, &aclConnectionElement) != CM_ERROR)
    {
        cmL2caConnInstType *l2caLink = theElement->cmL2caConnInst;

        if (l2caLink->state == CSR_BT_CM_L2CAP_STATE_CONNECTED)
        {
            CsrBtResultCode resultCode;
            CsrUint8 newMode = CSR_BT_ACTIVE_MODE;

            /* Register the vote. */
            l2caLink->requestedMode = cmPrim->requestedMode;

            /* By default the power table uses default sniff settings, only in case of a
             * sniff mode the settings needs to be checked for update. */
            resultCode = CmDmUpdatePowerTable(cmData,
                                              &l2caLink->deviceAddr,
                                              cmPrim->requestedMode,
                                              &newMode,
                                              CM_PT_STATE_L2CAP_CONNECT_OR_DISCONNECT);

            if (resultCode == RESULT_CODE_CM_POWER_TABLE_UPDATED)
            {
                /* Power table update is required, wait for the mode change event. */
                unlock = FALSE;
            }
            else if (resultCode == CSR_BT_RESULT_CODE_CM_COMMAND_DISALLOWED)
            {
                resultCode = csrBtReturnResultCode(newMode);

                CsrBtCmL2caModeChangeIndMsgSend(l2caLink,
                                                aclConnectionElement->interval,
                                                aclConnectionElement->mode,
                                                resultCode,
                                                CSR_BT_SUPPLIER_CM);
            }
            else
            {
                /* Inform this event to application. */
                CsrBtCmL2caModeChangeIndMsgSend(l2caLink,
                                                aclConnectionElement->interval,
                                                aclConnectionElement->mode,
                                                resultCode,
                                                CSR_BT_SUPPLIER_CM);
            }
        }
    }

    if (unlock)
    {
        /* Restore the local service manager queue */
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }
}
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
CsrBool CmDmPowerTableHandleBnepConnectedState(cmInstanceData_t *cmData, bnepTable *bnepConnection, CsrUint8 requestedMode)
{
    CsrBtResultCode resultCode;
    aclTable *aclConnectionElement;
    CsrUint8 newMode = CSR_BT_ACTIVE_MODE;
    CsrBool ptChangeRequired = FALSE;

    if (returnAclConnectionElement(cmData, bnepConnection->deviceAddr, &aclConnectionElement) != CM_ERROR)
    {
        /* Register BNEP vote. */
        bnepConnection->requestedMode = requestedMode;
        resultCode = CmDmUpdatePowerTable(cmData,
                                          &bnepConnection->deviceAddr,
                                          requestedMode,
                                          &newMode,
                                          CM_PT_STATE_BNEP_CONNECT_OR_DISCONNECT);

        if (resultCode == RESULT_CODE_CM_POWER_TABLE_UPDATED)
        {
            /* Do not unlock as we need for the power table to get updated in the lower layer. */
            ptChangeRequired = TRUE;
        }
        else if (resultCode == CSR_BT_RESULT_CODE_CM_COMMAND_DISALLOWED)
        {
            resultCode = csrBtReturnResultCode(newMode);

            csrBtCmBnepModeChangeIndMsgSend(cmData->bnepVar.appHandle,
                                            bnepConnection,
                                            aclConnectionElement->interval,
                                            aclConnectionElement->mode,
                                            resultCode,
                                            CSR_BT_SUPPLIER_CM);
        }
        else
        {
            /* Report to the application.s */
            csrBtCmBnepModeChangeIndMsgSend(cmData->bnepVar.appHandle,
                                            bnepConnection,
                                            aclConnectionElement->interval,
                                            aclConnectionElement->mode,
                                            resultCode,
                                            CSR_BT_SUPPLIER_CM);
        }
    }

    return ptChangeRequired;
}

void CsrBtCmBnepModeChangeReqHandler(cmInstanceData_t *cmData)
{
    CsrUint8 theIndex;
    CsrBtCmBnepModeChangeReq *cmPrim;

    cmPrim        = (CsrBtCmBnepModeChangeReq *) cmData->recvMsgP;
    theIndex      = returnBnepIdIndex(cmData, cmPrim->id);

    if (theIndex != CM_ERROR)
    {
        bnepTable *bnepConnection = &(cmData->bnepVar.connectVar[theIndex]);

        if (bnepConnection->state == CSR_BT_CM_BNEP_STATE_CONNECTED)
        {
            if (CmDmPowerTableHandleBnepConnectedState(cmData, bnepConnection, cmPrim->requestedMode))
            {
                /* Power table change is required, SM queue will be unlocked by respective mode change indication. */
                return;
            }
        }
    }

    /* Restore the local service manager queue */
    CsrBtCmServiceManagerLocalQueueHandler(cmData);
}
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

CsrUint8 CmDmGetActualMode(cmInstanceData_t *cmData, CsrBtDeviceAddr *deviceAddr)
{
    aclTable *aclConnectionElement;

    if (returnAclConnectionElement(cmData, *deviceAddr, &aclConnectionElement) != CM_ERROR)
    {
        return aclConnectionElement->mode;
    }

    return CSR_BT_ACTIVE_MODE;
}

void CmDmSwitchToActiveLinkPolicy(cmInstanceData_t *cmData,
                                  CsrBtDeviceAddr *deviceAddr,
                                  CsrBtCmPlayer player,
                                  CmPtState ptState,
                                  CsrUint8 *requestedMode)
{
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
    if (CmDmGetActualMode(cmData, deviceAddr) == CSR_BT_ACTIVE_MODE)
    {
        /* Link policy to the given address is set to ACTIVE. Here we need to ensure that
         * DM only accept ACTIVE policy till the calling procedure finishes.
         */
        CsrBtCmDmWriteLpSettingsReqMsgSend(*deviceAddr,
                                           player,
                                           CsrBtCmEnableMSSwitchParameter(cmData));
    }
    else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
    {
        /* The link policy against the given Bluetooth address must be
         * changed to ACTIVE, before CM can ensure that the DM only accept
         * ACTIVE link policy. */
        CmDmForceUpdatePowerTable(cmData,
                                  deviceAddr,
                                  CSR_BT_ACTIVE_MODE,
                                  NULL,
                                  ptState);

        if (requestedMode)
        {
            /* Update requestedMode for the caller in order to register it's vote for the CM's
             * voting process to determine the mode. */
            *requestedMode = cmData->smVar.requestedMode;
        }
    }
}
#endif /* !CSR_BT_INSTALL_LP_POWER_TABLE */

#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
static CsrBtCmPlayer cmDmGetPlayerFromPtState(CmPtState ptState)
{
    switch (ptState)
    {
        case CM_PT_STATE_L2CAP_CONNECT_OR_DISCONNECT:
            return L2CAP_PLAYER;
        case CM_PT_STATE_RFC_CONNECT_OR_DISCONNECT:
            return RFC_PLAYER;
        case CM_PT_STATE_SDC_SEARCH:
            return SDC_PLAYER;
        case CM_PT_STATE_LINK_POLICY_REQ:
        case CM_PT_STATE_LINK_POLICY_COMPLETE:
            return CM_LINK_POLICY_PLAYER;
        case CM_PT_STATE_LE_PHYSICAL_LINK_STATUS_REQ:
            return LE_PLAYER;
        case CM_PT_STATE_BNEP_CONNECT_OR_DISCONNECT:
            return BNEP_PLAYER;
        default:
            return UNDEFINED_PLAYER;
    }
}
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
static CsrBool cmDmIsAclConnectionLost(cmInstanceData_t *cmData)
{
    DM_HCI_MODE_CHANGE_EVENT_IND_T *dmPrim = (DM_HCI_MODE_CHANGE_EVENT_IND_T *)cmData->recvMsgP;

    switch (dmPrim->status)
    {
        case HCI_ERROR_NO_CONNECTION:
        case HCI_ERROR_CONN_TIMEOUT:
        case HCI_ERROR_OETC_USER:
        case HCI_ERROR_OETC_LOW_RESOURCE:
        case HCI_ERROR_OETC_POWERING_OFF:
        case HCI_ERROR_CONN_TERM_LOCAL_HOST:
            return TRUE;
    }

    return FALSE;
}

#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
static CsrBool cmPowerTableContinueProcessing(cmInstanceData_t *cmData)
{
    DM_HCI_MODE_CHANGE_EVENT_IND_T *dmPrim = (DM_HCI_MODE_CHANGE_EVENT_IND_T *)cmData->recvMsgP;
    CsrBool aclLost = (dmPrim->status == HCI_SUCCESS ? FALSE : cmDmIsAclConnectionLost(cmData));

    if (dmPrim->mode == CSR_BT_ACTIVE_MODE || aclLost)
    {
        return returnInitStateIndexToThisAddress(cmData,
                                                 dmPrim->bd_addr,
                                                 aclLost,
                                                 dmPrim->status == HCI_SUCCESS ? CSR_BT_RESULT_CODE_CM_SUCCESS : dmPrim->status,
                                                 dmPrim->status == HCI_SUCCESS ? CSR_BT_SUPPLIER_CM : CSR_BT_SUPPLIER_HCI);
    }

    return FALSE;
}
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */

static void cmPowerTableModeChangeCompleteHandler(cmInstanceData_t *cmData)
{
    DM_HCI_MODE_CHANGE_EVENT_IND_T *dmPrim = (DM_HCI_MODE_CHANGE_EVENT_IND_T *)cmData->recvMsgP;
    CsrBool smUnlock = TRUE, dmUnlock = FALSE, ptStateReset = TRUE;
    CsrBool aclLost = (dmPrim->status == HCI_SUCCESS ? FALSE : cmDmIsAclConnectionLost(cmData));

    switch (CM_PT_STATE_GET(cmData))
    {
        case CM_PT_STATE_RFC_CONNECT_OR_DISCONNECT:
        case CM_PT_STATE_L2CAP_CONNECT_OR_DISCONNECT:
        case CM_PT_STATE_BNEP_CONNECT_OR_DISCONNECT:
        case CM_PT_STATE_LINK_POLICY_REQ:
        case CM_PT_STATE_LE_PHYSICAL_LINK_STATUS_REQ:
        {
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
            if (cmPowerTableContinueProcessing(cmData))
            {
                /* Note: this call needs access to DM queue, make sure DM queue is not locked in the same context. */
                CsrBtCmDmWriteLpSettingsReqMsgSend(dmPrim->bd_addr,
                                                   cmDmGetPlayerFromPtState(CM_PT_STATE_GET(cmData)),
                                                   CsrBtCmEnableMSSwitchParameter(cmData));
                /* ptState will be reset while handling CSR_BT_CM_DM_WRITE_LP_SETTINGS_REQ prim. */
                ptStateReset = FALSE;

                /* Unlocking SM and DM queue will be taken care by the respective LP settings handler. */
                smUnlock = FALSE;
            }
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
            break;
        }

        case CM_PT_STATE_LINK_POLICY_COMPLETE:
        {
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
            if (cmPowerTableContinueProcessing(cmData))
            {
                /* This call assumes the DM lock. */
                CsrBtCmDmWriteLpSettingsOnlyMssMsgSend(cmData,
                                                       dmPrim->bd_addr,
                                                       cmDmGetPlayerFromPtState(CM_PT_STATE_GET(cmData)));
                /* Unlocking SM and DM queue will be taken care by the respective LP settings handler. */
                smUnlock = FALSE;
            }
            else
#endif
            {
                /* Since owner of CM_PT_STATE_LINK_POLICY_COMPLETE has locked DM queue, unlock it here
                 * in order to mark the end of link policy change command. */
                dmUnlock = TRUE;
            }
            break;
        }

        case CM_PT_STATE_APP:
        {
            /* The CM tries to exit sniff mode, because an application has
             * requested to goto ACTIVE mode or re-enter sniff mode with
             * different parameter. Inform the application of the result */
            csrBtCmModeChangeCfmMsgSend(cmData,
                                        dmPrim->length,
                                        dmPrim->mode,
                                        dmPrim->status == HCI_SUCCESS ? CSR_BT_RESULT_CODE_CM_SUCCESS: dmPrim->status,
                                        dmPrim->status == HCI_SUCCESS ? CSR_BT_SUPPLIER_CM: CSR_BT_SUPPLIER_HCI);
            break;
        }

        case CM_PT_STATE_SDC_SEARCH:
        {
            if (cmPowerTableContinueProcessing(cmData))
            {
                /* Continue with the SDC processing. */
                CsrBtCmSdcStartHandler(cmData, dmPrim->bd_addr);
                /* SM queue needs to stay locked for the SDC procedure. */
                smUnlock = FALSE;
            }
            break;
        }

        case CM_PT_STATE_SCO_CONNECT:
        {
            if (dmPrim->status == HCI_SUCCESS)
            {
                /* Note that the DM queue is locked by the respective caller. */
                csrBtCmDmInitRfcScoStateHandler(cmData, dmPrim->bd_addr);
            }
            else
            {
                /* The CM tries to exit sniff mode, because an application has
                 * requested it to initiate a SCO connection. The reason for
                 * exit sniff in the first place is that CM wants to be MASTER
                 * before making the SCO connection. If the mode is different from
                 * sniff we must consider the ACL as closed. Just restore the DM
                 * queue to prevent a deadlock, because the DM queue is taken by
                 * the CSR_BT_CM_SCO_CONNECT_REQ
                 * message. No need to send a confimation message to the
                 * application as it must have or will very soon received a
                 * CSR_BT_CM_L2CA_DISCONNECT_IND or a CSR_BT_CM_DISCONNECT_IND. */
                dmUnlock = TRUE;
            }

            /* SCO requests doesn't lock SM queue, hence no need to unlock it. */
            smUnlock = FALSE;
            break;
        }

        case CM_PT_STATE_ROLE_SWITCH:
        {
            if (dmPrim->status == HCI_SUCCESS)
            {
                switchRoleStateHandler(cmData, dmPrim->mode, dmPrim->bd_addr);
            }
            else
            {
                /* Found out if another ACL connection need to make a role switch */
                nextSwitchRoleHandler(cmData);
            }

            /* Success or failure we are continuing with the role switch procedure,
             * both SM and DM queue shall be freed by the respective role switch or
             * LP settings complete handlers. */
            smUnlock = FALSE;
            break;
        }

        default:
            break;
    }

    /* Reset the state to none as the power table change request is completed, unless prohibited. */
    if (ptStateReset)
    {
        CM_PT_STATE_SET(cmData, CM_PT_STATE_IDLE);
    }

    if (smUnlock)
    {
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }

    if (dmUnlock)
    {
        CsrBtCmDmLocalQueueHandler();
    }

    if (dmPrim->status != HCI_SUCCESS && !aclLost)
    {
        /* Mode change failure only needs to be provided if the underlying ACL link is still intact. */
        CsrUint8 requestedMode = CsrBtCmDmReturnRequestedMode(cmData, dmPrim->bd_addr);
        cmDmNotifyModeChangeErrorToApp(cmData,
                                       dmPrim,
                                       requestedMode,
                                       (CsrBtResultCode)dmPrim->status,
                                       CSR_BT_SUPPLIER_HCI);
    }
    else
    {
        /* In case if the mode change is successfull, since the ACL connection is lost,
         * no need to inform the application regarding mode change as applications
         * will receive disconnect message. */
    }
}
#endif

void CsrBtCmDmHciModeChangeEventHandler(cmInstanceData_t *cmData)
{
    DM_HCI_MODE_CHANGE_EVENT_IND_T *dmPrim = (DM_HCI_MODE_CHANGE_EVENT_IND_T *) cmData->recvMsgP;

#ifdef CSR_BT_INSTALL_CM_EVENT_MASK_SUBSCRIBE_MODE_CHANGE
    CsrBtCmPropgateEvent(cmData,
                         CsrBtCmPropgateModeChangeEvents,
                         CSR_BT_CM_EVENT_MASK_SUBSCRIBE_MODE_CHANGE,
                         dmPrim->status,
                         dmPrim,
                         NULL);
#endif

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
    /* Store the current mode */
    csrBtCmDmStoreActualMode(cmData);

    if (dmPrim->status == HCI_SUCCESS)
    {
        /* Inform applications regarding successfull change in mode. */
        cmDmNotifyModeChangeToApp(cmData,
                                  dmPrim->bd_addr,
                                  dmPrim->mode,
                                  dmPrim->length);
    }

    if (CM_PT_STATE_GET(cmData) != CM_PT_STATE_IDLE &&
        CsrBtBdAddrEq(&(dmPrim->bd_addr), &(cmData->smVar.operatingBdAddr)))
    {
        if (cmData->smVar.requestedMode == dmPrim->mode)
        {
            /* Mode change is received for the intended bd address and it matches
             * the mode which was requested by our module. */
            cmPowerTableModeChangeCompleteHandler(cmData);
        }
        else
        {
            if (dmPrim->status == HCI_SUCCESS)
            {
                /* The intended mode for us is not reached, this could happen
                 * if remote device has updated the mode. Retry power table update. */
                CmDmForceUpdatePowerTable(cmData,
                                          &dmPrim->bd_addr,
                                          cmData->smVar.requestedMode,
                                          NULL,
                                          CM_PT_STATE_GET(cmData));
            }
            else
            {
                if (!cmDmIsAclConnectionLost(cmData) &&
                    dmPrim->mode == CSR_BT_SNIFF_MODE)
                {
                    /* ACL connection is present and we had requested for ACTIVE mode, retry our mode change. */
                    CmDmForceUpdatePowerTable(cmData,
                                              &dmPrim->bd_addr,
                                              cmData->smVar.requestedMode,
                                              NULL,
                                              CM_PT_STATE_GET(cmData));
                }
                else
                {
                    /* Mode change has failed, ACL connection is lost, report this to application.
                     * Internal CM structures will be cleared when respective service and
                     * ACL disconnections are received. */
                    cmPowerTableModeChangeCompleteHandler(cmData);
                }
            }
        }
    }
#endif /* !CSR_BT_INSTALL_LP_POWER_TABLE */
}

